package com.example.ejb;

import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.jboss.ejb3.annotation.LocalBinding;
import org.jboss.ejb3.annotation.SecurityDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.ejb.exceptions.DatabaseException;
import com.example.ejb.exceptions.UserAlreadyExistsException;
import com.example.ejb.exceptions.UserDoesNotExistException;
import com.example.ejb.model.DbUser;

/**
 * Session Bean implementation class UsersStorageBean
 */
@Stateless
@LocalBinding(jndiBinding = UsersConnection.JNDI_NAME)
@SecurityDomain("EJB3Security")
public class UsersConnectionBean implements UsersConnection {
	private static final Logger logger = LoggerFactory
			.getLogger(UsersConnectionBean.class);

	/**
	 * Entity Manager injected by the container - persistence-unit must match name in
	 * persistence.xml
	 */
	@PersistenceContext(unitName = "JUnitDemo")
	private EntityManager entityManager;

	@Override
	@SuppressWarnings("unchecked")
	public List<DbUser> getAllUsers() {
		Query query = entityManager.createQuery("SELECT u FROM DbUser u");
		List<DbUser> resultList = query.getResultList();
		logger.debug("All users : " + resultList);
		return resultList;
	}

	@Override
	public DbUser getUserByDbId(long userDbId) throws DatabaseException {
		logger.info("retrieve user with DbId={}", userDbId);
		try {
			DbUser user = entityManager.find(DbUser.class, userDbId);
			logger.info("return user : {}", user);
			return user;
		} catch (Exception e) {
			logger.error("Error while trying to retrieve the user with id=" + userDbId, e);
			throw new DatabaseException("Exception encounted during user search", e);
		}
	}

	@Override
	@RolesAllowed("admin")
	public void addUser(DbUser user) throws UserAlreadyExistsException, DatabaseException {
		logger.info("Add user {}" + user);
		checkThatUserCanBeAdded(user);

		try {
			entityManager.persist(user);
			entityManager.flush();
			logger.debug("Successfully added user {}" + user);
		} catch (Exception e) {
			logger.error("Error while trying to add user " + user, e);
			throw new DatabaseException("Exception encounted during user addition : "
					+ e.getMessage(), e);
		}
	}

	@Override
	@RolesAllowed({ "admin", "operator" })
	public void updateUser(DbUser user) throws UserDoesNotExistException,
			DatabaseException {
		logger.info("Update user {}" + user);
		checkThatUserCanBeUpdated(user);

		try {
			entityManager.find(DbUser.class, user.getDbId());
			entityManager.merge(user);
			entityManager.flush();
			logger.debug("Successfully updated user {}" + user);
		} catch (Exception e) {
			logger.error("Error while trying to update user " + user, e);
			throw new DatabaseException("Exception encounted during user update", e);
		}
	}

	@Override
	@RolesAllowed("admin")
	public void deleteUser(long userDbId) throws UserDoesNotExistException,
			DatabaseException {
		DbUser user = getUserForDelete(userDbId);

		try {
			entityManager.remove(user);
			entityManager.flush();
		} catch (Exception e) {
			logger.error("Error while trying to update user " + user, e);
			throw new DatabaseException("Exception encounted during user deletion", e);
		}
	}

	private void checkThatUserCanBeAdded(DbUser user) throws DatabaseException,
			UserAlreadyExistsException {
		checkForNull(user);

		long dbId = user.getDbId();
		if ((dbId > 0) && (getUserByDbId(dbId) != null)) {
			logger.error("The user with id = {} already exists in the database", dbId);
			throw new UserAlreadyExistsException();
		}
	}

	private void checkThatUserCanBeUpdated(DbUser user) throws DatabaseException,
			UserDoesNotExistException {
		checkForNull(user);

		long dbId = user.getDbId();
		if ((dbId == 0) || (getUserByDbId(dbId) == null)) {
			logger.error("The user with id = {} already does not exist in the database",
					dbId);
			throw new UserDoesNotExistException();
		}
	}

	private void checkForNull(DbUser user) {
		if (user == null) {
			logger.error("The user is null");
			throw new IllegalArgumentException("The user must not be null");
		}
	}

	private DbUser getUserForDelete(long userDbId) throws DatabaseException,
			UserDoesNotExistException {
		if (userDbId <= 0) {
			logger.error("Cannot delete a user with id={}", userDbId);
			throw new IllegalArgumentException("The user id not be positive");
		}

		DbUser user = getUserByDbId(userDbId);
		if (user == null) {
			logger.error("There is no user with id={}", userDbId);
			throw new UserDoesNotExistException();
		}

		return user;
	}

}
